from flask import Flask,request,Response,jsonify,render_template
from flask_cache import Cache
from werkzeug.utils import secure_filename
import pymysql
import json
import base64
import os
import sys
import datetime
import shutil


mypath = os.path.dirname(os.path.abspath(sys.argv[0]))


PAGE = 20
DOWNLOAD   = os.path.join(mypath, 'download')
ALLOWED_EXTENSIONS = ['exe','apk','hap']
PACK = ["win32.exe","android.apk","hmos.hap","win64.exe"]
STATIC     = os.path.join(mypath, 'static')

templates  = os.path.join(mypath, 'templates')



app = Flask(__name__, template_folder=templates, static_folder=STATIC)
cache = Cache(app,config={'CACHE_TYPE': 'simple'})


class mysql(object):
    def __init__(self, host='127.0.0.1', port=3306, user='root', passwd='anjiu123', datadb='anjiu'):
        self.host = host
        self.port = port
        self.user = user
        self.passwd = passwd
        self.datadb = datadb
        self._connect()
            
    def _connect(self):
        try:
            self.db = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.passwd, database=self.datadb)
            self.cursor = self.db.cursor()
        except pymysql.MySQLError:
            pass

    def _ping(self):
        try:
            self.db.ping(reconnect=True)
        except pymysql.MySQLError:
            self._connect()

    def see_sj(self):
        sql = "SELECT mid, name, title, synopsis, mdatetime FROM child_account ORDER BY RAND() LIMIT 4"
        if self.sql_run(sql):
            return self.cursor.fetchall()
        return []

    def see_mid(self, mid):
        sql = "SELECT name, title, synopsis, data, mdatetime FROM child_account WHERE mid='{}'".format(mid)
        if self.sql_run(sql):
            return self.cursor.fetchall()
        return []

    def get_sum(self):
        sql  = "SELECT id FROM child_account"
        _sum = self.sql_run(sql)
        return _sum

    def see_fei(self, dum):
        sql = "SELECT mid, name, title, synopsis, mdatetime FROM child_account ORDER BY id DESC LIMIT {} OFFSET {}".format(PAGE, dum)
        if self.sql_run(sql):
            return self.cursor.fetchall()
        return []

    def search_see(self,c,m):
        if m == 0:
            sql = "SELECT mid, title, mdatetime, version FROM child_account WHERE CONCAT(title) LIKE '%{}%'".format(c)
        elif m == 1:
            sql = "SELECT mid, title, mdatetime, version FROM child_account WHERE mid='{}'".format(c)
        elif m == 2:
            sql = "SELECT mid, title, mdatetime, version FROM child_account WHERE CONCAT(mid) LIKE '%{}%'".format(c)
        if self.sql_run(sql):
            return self.cursor.fetchall()
        return []

    def version_see(self,mid):
        sql = "SELECT id, version FROM child_account WHERE mid='{}'".format(mid)
        if self.sql_run(sql):
            return self.cursor.fetchall()
        return []

    def del_data(self, mid):
        sql = "DELETE FROM child_account WHERE mid='{}'".format(mid)
        return self.sql_run(sql)

    def add_data(self, mid, name, title, synopsis, data, version):
        mdatetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        sql = "INSERT INTO child_account(id, mid, name, title, synopsis, data, mdatetime, version) VALUES (null, '{}', '{}', '{}', '{}', '{}', '{}', '{}')".format(mid, name, title, synopsis, data, mdatetime, version)
        return self.sql_run(sql)

    def set_data(self,name,title,synopsis,data,version,mid):
        mdatetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        sql = "UPDATE child_account set name='{}', title='{}', synopsis='{}', data='{}', mdatetime='{}', version='{}' WHERE mid='{}'".format(name,title,synopsis,data,mdatetime,version,mid)
        ret = self.sql_run(sql)
        if ret:
            return self.cursor.rowcount

    def sql_run(self,sql):
        self._ping()
        try:
            res = self.cursor.execute(sql)
            self.db.commit()
            return res
        except pymysql.MySQLError as e:
            print("sql err:", e)
            return None



class formexat():
    def __init__(self):
        self.abc = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]

    def letter_digital(self,a):
        y=1
        s=0
        while True:
            if y > len(a):
                return s
            s = s + (26**(len(a)-y))*(self.abc.index(a[y-1])+1)
            y += 1

    def digital_letter(self,a):
        s = ""
        y = a%26
        while True:
            s = self.abc[y-1] + s
            if a <= 26:
                return s
            if a%26:
                a = int(a//26)
            else: 
                a = int(a//26) -1
            y = a%26



msql = mysql()



@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html',mdata="404错误！",msg="看来我们找不到你要找的那一页"), 404


@app.errorhandler(500)
def internal_server_error(e):
    return render_template('404.html',mdata="500错误！",msg="服务器好像出了问题"), 500


@app.route('/', methods=['GET'])
def indexs():
    ret = msql.see_sj()
    return render_template("index.html", mdata=ret)


@app.route('/single/<url>', methods=['GET'])
def single(url):
    ret = msql.see_mid(url)
    if ret:
        jsd3 = json.loads(base64.b64decode(ret[0][3]))
        addr = []
        if os.path.exists(os.path.join(DOWNLOAD, url, PACK[0])):
            addr.append(("win32", PACK[0]))
        if os.path.exists(os.path.join(DOWNLOAD, url, PACK[3])):
            addr.append(("win64", PACK[3]))
        if os.path.exists(os.path.join(DOWNLOAD, url, PACK[1])):
            addr.append(("安卓", PACK[1]))
        if os.path.exists(os.path.join(DOWNLOAD, url, PACK[2])):
            addr.append(("鸿蒙", PACK[2]))
        return render_template("single.html", mdata=(ret[0][0],ret[0][1],ret[0][2],jsd3,ret[0][4],url,addr))
    else:
        return render_template('404.html',mdata="查不到ID",msg="数据库没有此数据")


@app.route('/categories', methods=['GET'])
def categories():
    try:
        page = int(request.args.get("page"))
        summax = int(request.args.get("pagesum"))
    except:
        page = 0 
        summax = msql.get_sum()//PAGE + 1
    msum = [i for i in range(page-2, summax)]
    if 0 in msum:
        msum = msum[msum.index(0):msum.index(0)+5]
    else:
        msum = msum[:5]
    ret = msql.see_fei(page*PAGE)
    #ret = [(v[0], v[1], v[2], v[3], v[4], json.loads(base64.b64decode(v[5]))) for v in ret]
    return render_template("categories.html", mdata=ret, page=page, msum=msum, summax=summax)



@app.route('/search', methods=['GET'])
def search():
    c = request.args.get("search")
    m = 0
    if c[:2] == "\\d" or c[:2] == "\\a":
        if c[:2] == "\\d":
            m = 1 
        else:
            m = 2
        c = c[2:]   
    ret = msql.search_see(c,m) 
    return render_template("list.html", mdata=ret)



@app.route('/update', methods=['GET'])
def update():
    return render_template("upload.html")



@app.route('/download/<mid>/<name>', methods=['GET','POST'])
def download(mid,name):
    path_fie = os.path.join(DOWNLOAD, mid, name)
    if os.path.exists(path_fie):
        size = os.path.getsize(path_fie)
        response = Response(file_send(path_fie), content_type='application/octet-stream')
        response.headers["Content-Disposition"] = 'attachment; filename=%s' % name
        response.headers["Content-Length"] = size
        response.headers["get-post"] = "true"
    else:
        response = Response("找不到文件,已被删除")
        response.headers["get-post"] = "false"
    return response


@app.route('/version', methods=['POST'])
def version():
    mid = request.args.get("mid")
    ret = msql.version_see(mid)
    if ret:
        return jsonify({'code': "1", "version":ret[0][0]})
    return jsonify({'code': "0", "msg":"数据不存在"})


@app.route("/upload", methods=["POST"])
def html_upload():
    obj = request.files.get('fileselect')
    pack = secure_filename(obj.filename).split(".")[-1]
    if pack not in ALLOWED_EXTENSIONS:
        return "禁止上传的类型文件"
    title = request.values.get("filename")
    synop = request.values.get("content")

    mid = ''.join(reversed(datetime.datetime.now().strftime("%Y%m%d%H%M%S%f%u%H")))
    img_path  = os.path.join(STATIC, "images", mid)
    file_path = os.path.join(DOWNLOAD, mid)
    os.mkdir(img_path)
    os.mkdir(file_path)
    shutil.copy(os.path.join(STATIC, "images", "ze.jpg"), img_path)
    shutil.copy(os.path.join(STATIC, "images", "cx.jpg"), img_path)
    obj.save(os.path.join(DOWNLOAD, mid, PACK[ALLOWED_EXTENSIONS.index(pack)]))
    data = base64.b64encode(json.dumps({"1":"新程序","2":"待增加","3":"待续。。。"}).encode('utf-8')).decode('utf-8')
    if msql.add_data(mid, "无名", title[:30], synop[:200], data, "0.0.1"):
        flask_clear()
        return "上传成功...  程序ID值：" + mid
    shutil.rmtree(file_path)
    shutil.rmtree(img_path)
    return "上传失败..."


@app.route("/update", methods=["POST"])
def requests_upload():
    mid       = request.values.get('mid')
    if not mid:
        mid = ''.join(reversed(datetime.datetime.now().strftime("%Y%m%d%H%M%S%f%u%H")))
    name      = request.values.get('name')
    title     = request.values.get('title')
    synop     = request.values.get('synop')
    data      = request.values.get('data')
    version   = request.values.get('version')
    img_path  = os.path.join(STATIC, "images", mid)
    file_path = os.path.join(DOWNLOAD, mid)

    if not os.path.exists(img_path):
        os.mkdir(img_path)

    file_write(os.path.join(img_path, "ze.jpg"), request.files.get('img1'))
    file_write(os.path.join(img_path, "cx.jpg"), request.files.get('img2'))
    if not os.path.exists(os.path.join(img_path, "ze.jpg")):
        shutil.copy(os.path.join(STATIC, "images", "ze.jpg"), img_path)
    if not os.path.exists(os.path.join(img_path, "cx.jpg")):
        shutil.copy(os.path.join(STATIC, "images", "cx.jpg"), img_path)

    if not os.path.exists(file_path):
        os.mkdir(file_path)
    file_write(os.path.join(file_path, PACK[0]), request.files.get('win32'))
    file_write(os.path.join(file_path, PACK[3]), request.files.get('win64'))
    file_write(os.path.join(file_path, PACK[1]), request.files.get('android'))
    file_write(os.path.join(file_path, PACK[2]), request.files.get('hmos'))

    if not name:
        name = "无名"
    if request.values.get('mid'):
        if msql.set_data(name[:20], title[:30], synop[:200], data[:240], version[:10], mid):
            flask_clear()
            return jsonify({'code': "1", "msg":mid})
    else:
        if not version:
            version = "0.0.1"
        if msql.add_data(mid, name[:20], title[:30], synop[:200], data[:240], version[:10]):
            flask_clear()
            return jsonify({'code': "1", "msg":mid})
    shutil.rmtree(file_path)
    shutil.rmtree(img_path)
    return jsonify({'code': "0", "msg":"数据提交失败"})


@app.route("/remove", methods=["GET","POST"])
def remove_data():
    mid  = request.values.get('mid')
    amd  = request.values.get('mid-amd')
    ase  = request.values.get('ase-amd')
    a = formexat()
    adm = ''.join(reversed(datetime.datetime.now().strftime("%Y%Y%m%m%H%H%M%M%H%H")))
    if str(a.letter_digital(amd)) == mid and str(a.letter_digital(ase)) == adm:
        msql.del_data(mid)
        img_path  = os.path.join(STATIC, "images", mid)
        file_path = os.path.join(DOWNLOAD, mid)
        if os.path.exists(file_path):
            shutil.rmtree(file_path)
        if os.path.exists(img_path):
            shutil.rmtree(img_path)
        flask_clear()
        return "删除成功"    
    return render_template('404.html',mdata="404错误！",msg="看来我们找不到你要找的那一页"), 404



def flask_clear():
    with app.app_context():
        cache.clear()


def file_write(filepath, fileobj):
    if not fileobj:
        return
    with open(filepath, "wb") as f:
        while True:
            chunk = fileobj.read(4096)
            if not chunk:
                return filepath
            f.write(chunk)


def file_send(store):
    with open(store, 'rb') as file_:
        while 1:
            data = file_.read(30 * 1024 * 1024)
            if not data:
                break
            yield data



if __name__ == "__main__":
    app.run(port=5000)